# coding=utf-8

from flask import jsonify, request, current_app, render_template, session
from flask_login import login_required
import json
from apscheduler.triggers.cron import CronTrigger
from datetime import datetime, timedelta

from app.cores.dictionaries import (STATUS, DISPATCHER_STATUS, ELEMENT_TYPE, DISPATCHER_TYPE, CASE_TYPE, REPORT_RESULT,
                                    EXPECTATION_LOGIC, LOGIC_CONTROLLER_TYPE, TOOL_TYPE, CONTENT_TYPE)
from app.cores.case.http.dispatcher import HTTPCaseDispatcher
from app.cores.case.http.http_cookie_pool_manager import HTTPCookiePoolManager
from app.cores.case.ssh.dispatcher import SSHCaseDispatcher
from app.cores.case.sql.dispatcher import SQLCaseDispatcher
from app.cores.case.debug.dispatcher import DebugCaseDispatcher
from app.cores.tool.dispatcher import ScriptToolDispatcher
from app.cores.dispatcher import async_module_run, async_project_run, apscheduler_async_project_run
from app.routes.ajax import bp
from app.utils.util import get_form_from_request, exception_handle, get_project_id_from_current_element
from app.models import Case, Project, Module, Scene, HTTPCase, SSHCase, SQLCase, Dispatcher, DispatcherDetail, Report, \
    SubElementInLogicController, LogicController, IfController, WhileController, LoopController, SimpleController, \
    Scheduler, DingTalkRobotSetting, DingTalkRobotSettingAssociationProject, DebugCase, Tool, TimerTool, ScriptTool, \
    VariableDefinitionTool, EmailReceiverSetting, EmailReceiverSettingAssociationProject, HTTPHeaderManagerTool, \
    HTTPCookieManagerTool
from app.template_global import render_to_json
from app.extensions import dispatcher_scheduler
from app.template_global import sort_by_order_in_project, sort_by_order_in_module, sort_by_order_in_logic_controller


@bp.route('/case/http/save', methods=['POST'])
@login_required
def save_http_case():
    # 参数校验
    exist, form_id = get_form_from_request(request, 'id')
    if not exist: return form_id
    exist, form_name = get_form_from_request(request, 'name')
    if not exist: return form_name
    exist, form_description = get_form_from_request(request, 'description')
    if not exist: return form_description
    exist, form_protocol = get_form_from_request(request, 'protocol')
    if not exist: return form_protocol
    exist, form_domain = get_form_from_request(request, 'domain')
    if not exist: return form_domain
    exist, form_port = get_form_from_request(request, 'port')
    if not exist: return form_port
    exist, form_method = get_form_from_request(request, 'method')
    if not exist: return form_method
    exist, form_path = get_form_from_request(request, 'path')
    if not exist: return form_path
    exist, form_content_encoding = get_form_from_request(request, 'content_encoding')
    if not exist: return form_content_encoding
    exist, form_expectation_logic = get_form_from_request(request, 'expectation_logic')
    if not exist: return form_expectation_logic
    exist, form_content_type = get_form_from_request(request, 'content_type')
    if not exist: return form_content_type
    exist, form_param_json = get_form_from_request(request, 'param_json')
    if not exist: return form_param_json
    exist, form_header_json = get_form_from_request(request, 'header_json')
    if not exist: return form_header_json
    exist, form_file_upload_json = get_form_from_request(request, 'file_upload_json')
    if not exist: return form_file_upload_json
    exist, form_expectation_json = get_form_from_request(request, 'expectation_json')
    if not exist: return form_expectation_json
    exist, form_message_body_json = get_form_from_request(request, 'message_body_json')
    if not exist: return form_message_body_json
    exist, form_preprocessor_script = get_form_from_request(request, 'preprocessor_script')
    if not exist: return form_preprocessor_script
    exist, form_postprocessor_script = get_form_from_request(request, 'postprocessor_script')
    if not exist: return form_postprocessor_script
    try:
        params = json.loads(form_param_json)
        headers = json.loads(form_header_json)
        file_upload = json.loads(form_file_upload_json)
        expectations = json.loads(form_expectation_json)
        if not Case.exist_and_status_not_delete(id=form_id):
            return jsonify({
                'error_no': -1,
                'error_msg': '错误信息 [' + '%s编号案例不存在或已删除' % form_id + ']',
            })
        HTTPCase.update(
            id=form_id,
            name=form_name,
            description=form_description,
            protocol=form_protocol,
            domain=form_domain,
            port=form_port,
            method=form_method,
            path=form_path,
            encoding=form_content_encoding,
            expectation_logic=form_expectation_logic,
            params=params,
            headers=headers,
            file_upload=file_upload,
            expectations=expectations,
            message_body=form_message_body_json,
            content_type=form_content_type,
            preprocessor_script=form_preprocessor_script,
            postprocessor_script=form_postprocessor_script,
        )
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/case/http/execute', methods=['POST'])
def execute_http_case():
    # 参数校验
    exist, form_case_id = get_form_from_request(request, 'case_id')
    if not exist: return form_case_id
    if not Case.exist_and_status_not_delete(id=form_case_id):
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + '%s编号案例不存在或已删除' % form_case_id + ']',
        })
    http_case_dispatcher = None
    try:
        case = Case.query.filter_by(id=form_case_id, status=STATUS.NORMAL).first()
        http_case_dispatcher = HTTPCaseDispatcher(case=case, dispatcher_type=DISPATCHER_TYPE.DEBUG)
        result = http_case_dispatcher.run()
        request_ = result.get('request_')
        expectations_result = result.get('expectations_result')
        log_text = result.get('log_text')
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            # 'error_msg': '错误信息 [' + str(e.args) + ']',
            'error_msg': '错误信息 [' + repr(e) + ']',
            'log': http_case_dispatcher.dispatcher_logger.get_string_buffer() if http_case_dispatcher is not None else '',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'status_code': request_.response_status_code,
            # 'response_body': pprint.pformat(request_.response_body) if isinstance(request_.response_body, dict) else request_.response_body,
            'response_body': json.dumps(request_.response_body, ensure_ascii=False) if isinstance(request_.response_body, dict) else request_.response_body,
            # 'response_headers': render_dict_to_html(request_.response_headers),
            'response_headers': json.dumps(request_.response_headers, ensure_ascii=False),
            # 'request_body': render_dict_to_html(request_.request_body),
            'request_body': json.dumps(request_.request_body, ensure_ascii=False),
            # 'request_headers': render_dict_to_html(request_.request_headers),
            'request_headers': json.dumps(request_.request_headers, ensure_ascii=False),
            'assert_success': all([expectations_result, not case.specific_case.postprocessor_failure]),  # 期望断言结果和后处理断言结果
            'expectations_result': expectations_result,
            'postprocessor_failure': case.specific_case.postprocessor_failure,
            'postprocessor_failure_message': case.specific_case.postprocessor_failure_message,
            'expectations':  json.loads(render_to_json(case.specific_case.expectations)),
            'log': log_text,
        })


@bp.route('/case/http/cookie/get', methods=['POST'])
def get_cookie():
    try:
        rcj = HTTPCookiePoolManager.get_request_cookie_jar(type=DISPATCHER_TYPE.DEBUG)
        cookies = []
        if rcj is not None:
            for cookie in rcj:
                cookies.append(dict(
                    name=cookie.name,
                    value=cookie.value,
                    domain=cookie.domain,
                    # 被注释掉的几个字段是由于方法requests.cookies.create_cookie()无法接收这些参数,因此无需将这些数据存在前台表格中
                    # domain_specified=cookie.domain_specified,
                    # domain_initial_dot=cookie.domain_initial_dot,
                    path=cookie.path,
                    # path_specified=cookie.path_specified,
                    expires=cookie.expires,
                    secure=cookie.secure,
                    version=cookie.version,
                    port=cookie.port,
                    # port_specified=cookie.port_specified,
                    discard=cookie.discard,
                    comment=cookie.comment,
                    comment_url=cookie.comment_url,
                    rfc2109=cookie.rfc2109,
                    rest=cookie._rest,
                ))
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'cookies': cookies,
        })


@bp.route('/case/http/cookie/save', methods=['POST'])
def save_cookie():
    exist, form_cookies = get_form_from_request(request, 'cookies')
    if not exist: return form_cookies
    try:
        cookies = json.loads(form_cookies)
        rcj = HTTPCookiePoolManager.get_request_cookie_jar(type=DISPATCHER_TYPE.DEBUG)
        for cookie in cookies:
            if cookie.get('domain') is None:
                return jsonify({
                    'error_no': -1,
                    'error_msg': 'domain不能为None',
                })
            if 'Delete' in cookie: cookie.pop('Delete')
            if cookie['expires'] == '': cookie['expires'] = None
        HTTPCookiePoolManager.clear_and_reset(rcj=rcj, cookies=cookies)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'cookies': cookies,
        })


@bp.route('/case/ssh/save', methods=['POST'])
@login_required
def save_ssh_case():
    # 参数校验
    exist, form_id = get_form_from_request(request, 'id')
    if not exist: return form_id
    exist, form_name = get_form_from_request(request, 'name')
    if not exist: return form_name
    exist, form_description = get_form_from_request(request, 'description')
    if not exist: return form_description
    exist, form_host_name = get_form_from_request(request, 'host_name')
    if not exist: return form_host_name
    exist, form_port = get_form_from_request(request, 'port')
    if not exist: return form_port
    exist, form_connection_timeout = get_form_from_request(request, 'connection_timeout')
    if not exist: return form_connection_timeout
    exist, form_user_name = get_form_from_request(request, 'user_name')
    if not exist: return form_user_name
    exist, form_password = get_form_from_request(request, 'password')
    if not exist: return form_password
    exist, form_expectation_logic = get_form_from_request(request, 'expectation_logic')
    if not exist: return form_expectation_logic
    exist, form_command = get_form_from_request(request, 'command')
    if not exist: return form_command
    exist, form_expectation_json = get_form_from_request(request, 'expectation_json')
    if not exist: return form_expectation_json
    exist, form_preprocessor_script = get_form_from_request(request, 'preprocessor_script')
    if not exist: return form_preprocessor_script
    exist, form_postprocessor_script = get_form_from_request(request, 'postprocessor_script')
    if not exist: return form_postprocessor_script
    try:
        expectations = json.loads(form_expectation_json)
        if not Case.exist_and_status_not_delete(id=form_id):
            return jsonify({
                'error_no': -1,
                'error_msg': '错误信息 [' + '%s编号案例不存在或已删除' % form_id + ']',
            })
        SSHCase.update(
            id=form_id,
            name=form_name,
            description=form_description,
            host_name=form_host_name,
            port=form_port,
            connection_timeout=form_connection_timeout,
            user_name=form_user_name,
            password=form_password,
            command=form_command,
            expectation_logic=form_expectation_logic,
            expectations=expectations,
            preprocessor_script=form_preprocessor_script,
            postprocessor_script=form_postprocessor_script,
        )
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/case/ssh/execute', methods=['POST'])
def execute_ssh_case():
    # 参数校验
    exist, form_case_id = get_form_from_request(request, 'case_id')
    if not exist: return form_case_id
    if not Case.exist_and_status_not_delete(id=form_case_id):
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + '%s编号案例不存在或已删除' % form_case_id + ']',
        })
    ssh_case_dispatcher = None
    try:
        case = Case.query.filter_by(id=form_case_id, status=STATUS.NORMAL).first()
        ssh_case_dispatcher = SSHCaseDispatcher(case=case, dispatcher_type=DISPATCHER_TYPE.DEBUG)
        result = ssh_case_dispatcher.run()
        request_ = result.get('request_')
        expectations_result = result.get('expectations_result')
        log_text = result.get('log_text')
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
            'log': ssh_case_dispatcher.dispatcher_logger.get_string_buffer() if ssh_case_dispatcher is not None else '',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            # 'status_code': request_.response_status_code,
            # 'response_body': pprint.pformat(request_.response_body) if isinstance(request_.response_body, dict) else request_.response_body,
            'response_body': json.dumps(request_.response_body, ensure_ascii=False) if isinstance(request_.response_body, dict) else request_.response_body,
            # 'response_headers': render_dict_to_html(request_.response_headers),
            'response_headers': json.dumps(request_.response_headers, ensure_ascii=False),
            # 'request_body': render_dict_to_html(request_.request_body),
            'request_body': json.dumps(request_.request_body, ensure_ascii=False),
            # 'request_headers': render_dict_to_html(request_.request_headers),
            'request_headers': json.dumps(request_.request_headers, ensure_ascii=False),
            'assert_success': all([expectations_result, not case.specific_case.postprocessor_failure]),  # 期望断言结果和后处理断言结果
            'expectations_result': expectations_result,
            'postprocessor_failure': case.specific_case.postprocessor_failure,
            'postprocessor_failure_message': case.specific_case.postprocessor_failure_message,
            'expectations':  json.loads(render_to_json(case.specific_case.expectations)),
            'log': log_text,
        })


@bp.route('/case/sql/save', methods=['POST'])
@login_required
def save_sql_case():
    # 参数校验
    exist, form_id = get_form_from_request(request, 'id')
    if not exist: return form_id
    exist, form_name = get_form_from_request(request, 'name')
    if not exist: return form_name
    exist, form_description = get_form_from_request(request, 'description')
    if not exist: return form_description
    exist, form_host = get_form_from_request(request, 'host')
    if not exist: return form_host
    exist, form_port = get_form_from_request(request, 'port')
    if not exist: return form_port
    exist, form_connect_timeout = get_form_from_request(request, 'connect_timeout')
    if not exist: return form_connect_timeout
    exist, form_user = get_form_from_request(request, 'user')
    if not exist: return form_user
    exist, form_password = get_form_from_request(request, 'password')
    if not exist: return form_password
    exist, form_expectation_logic = get_form_from_request(request, 'expectation_logic')
    if not exist: return form_expectation_logic
    exist, form_sql = get_form_from_request(request, 'sql')
    if not exist: return form_sql
    exist, form_db_type = get_form_from_request(request, 'db_type')
    if not exist: return form_db_type
    exist, form_charset = get_form_from_request(request, 'charset')
    if not exist: return form_charset
    exist, form_expectation_json = get_form_from_request(request, 'expectation_json')
    if not exist: return form_expectation_json
    exist, form_preprocessor_script = get_form_from_request(request, 'preprocessor_script')
    if not exist: return form_preprocessor_script
    exist, form_postprocessor_script = get_form_from_request(request, 'postprocessor_script')
    if not exist: return form_postprocessor_script
    try:
        expectations = json.loads(form_expectation_json)
        if not Case.exist_and_status_not_delete(id=form_id):
            return jsonify({
                'error_no': -1,
                'error_msg': '错误信息 [' + '%s编号案例不存在或已删除' % form_id + ']',
            })
        SQLCase.update(
            id=form_id,
            name=form_name,
            description=form_description,
            host=form_host,
            port=form_port,
            connect_timeout=form_connect_timeout,
            user=form_user,
            password=form_password,
            sql=form_sql,
            charset=form_charset,
            db_type=form_db_type,
            expectation_logic=form_expectation_logic,
            expectations=expectations,
            preprocessor_script=form_preprocessor_script,
            postprocessor_script=form_postprocessor_script,
        )
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/case/sql/execute', methods=['POST'])
def execute_sql_case():
    # 参数校验
    exist, form_case_id = get_form_from_request(request, 'case_id')
    if not exist: return form_case_id
    if not Case.exist_and_status_not_delete(id=form_case_id):
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + '%s编号案例不存在或已删除' % form_case_id + ']',
        })
    sql_case_dispatcher = None
    try:
        case = Case.query.filter_by(id=form_case_id, status=STATUS.NORMAL).first()
        sql_case_dispatcher = SQLCaseDispatcher(case=case, dispatcher_type=DISPATCHER_TYPE.DEBUG)
        result = sql_case_dispatcher.run()
        request_ = result.get('request_')
        expectations_result = result.get('expectations_result')
        log_text = result.get('log_text')
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
            'log': sql_case_dispatcher.dispatcher_logger.get_string_buffer() if sql_case_dispatcher is not None else '',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            # 'status_code': request_.response_status_code,
            # 'response_body': pprint.pformat(request_.response_body) if isinstance(request_.response_body, dict) else request_.response_body,
            'response_body': json.dumps(request_.response_body, ensure_ascii=False, default=str) if isinstance(request_.response_body, dict) else request_.response_body,
            # 'response_headers': render_dict_to_html(request_.response_headers),
            'response_headers': json.dumps(request_.response_headers, ensure_ascii=False),
            # 'request_body': render_dict_to_html(request_.request_body),
            'request_body': json.dumps(request_.request_body, ensure_ascii=False),
            # 'request_headers': render_dict_to_html(request_.request_headers),
            'request_headers': json.dumps(request_.request_headers, ensure_ascii=False),
            'assert_success': all([expectations_result, not case.specific_case.postprocessor_failure]),  # 期望断言结果和后处理断言结果
            'expectations_result': expectations_result,
            'postprocessor_failure': case.specific_case.postprocessor_failure,
            'postprocessor_failure_message': case.specific_case.postprocessor_failure_message,
            'expectations':  json.loads(render_to_json(case.specific_case.expectations)),
            'log': log_text,
        })


@bp.route('/case/debug/save', methods=['POST'])
@login_required
def save_debug_case():
    # 参数校验
    exist, form_id = get_form_from_request(request, 'id')
    if not exist: return form_id
    exist, form_name = get_form_from_request(request, 'name')
    if not exist: return form_name
    exist, form_description = get_form_from_request(request, 'description')
    if not exist: return form_description
    exist, form_project_variable = get_form_from_request(request, 'project_variable')
    if not exist: return form_project_variable
    exist, form_expectation_logic = get_form_from_request(request, 'expectation_logic')
    if not exist: return form_expectation_logic
    exist, form_expectation_json = get_form_from_request(request, 'expectation_json')
    if not exist: return form_expectation_json
    exist, form_preprocessor_script = get_form_from_request(request, 'preprocessor_script')
    if not exist: return form_preprocessor_script
    exist, form_postprocessor_script = get_form_from_request(request, 'postprocessor_script')
    if not exist: return form_postprocessor_script
    try:
        expectations = json.loads(form_expectation_json)
        if not Case.exist_and_status_not_delete(id=form_id):
            return jsonify({
                'error_no': -1,
                'error_msg': '错误信息 [' + '%s编号案例不存在或已删除' % form_id + ']',
            })
        DebugCase.update(
            id=form_id,
            name=form_name,
            description=form_description,
            project_variable=True if form_project_variable.strip() == '是' else False,
            expectation_logic=form_expectation_logic,
            expectations=expectations,
            preprocessor_script=form_preprocessor_script,
            postprocessor_script=form_postprocessor_script,
        )
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/case/debug/execute', methods=['POST'])
def execute_debug_case():
    # 参数校验
    exist, form_case_id = get_form_from_request(request, 'case_id')
    if not exist: return form_case_id
    if not Case.exist_and_status_not_delete(id=form_case_id):
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + '%s编号案例不存在或已删除' % form_case_id + ']',
        })
    debug_case_dispatcher = None
    try:
        case = Case.query.filter_by(id=form_case_id, status=STATUS.NORMAL).first()
        debug_case_dispatcher = DebugCaseDispatcher(case=case, dispatcher_type=DISPATCHER_TYPE.DEBUG)
        result = debug_case_dispatcher.run()
        request_ = result.get('request_')
        expectations_result = result.get('expectations_result')
        log_text = result.get('log_text')
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
            'log': debug_case_dispatcher.dispatcher_logger.get_string_buffer() if debug_case_dispatcher is not None else '',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            # 'status_code': request_.response_status_code,
            # 'response_body': pprint.pformat(request_.response_body) if isinstance(request_.response_body, dict) else request_.response_body,
            'response_body': json.dumps(request_.response_body, ensure_ascii=False, default=str) if isinstance(request_.response_body, dict) else request_.response_body,
            # 'response_headers': render_dict_to_html(request_.response_headers),
            'response_headers': json.dumps(request_.response_headers, ensure_ascii=False),
            # 'request_body': render_dict_to_html(request_.request_body),
            'request_body': json.dumps(request_.request_body, ensure_ascii=False),
            # 'request_headers': render_dict_to_html(request_.request_headers),
            'request_headers': json.dumps(request_.request_headers, ensure_ascii=False),
            'assert_success': all([expectations_result, not case.specific_case.postprocessor_failure]),  # 期望断言结果和后处理断言结果
            'expectations_result': expectations_result,
            'postprocessor_failure': case.specific_case.postprocessor_failure,
            'postprocessor_failure_message': case.specific_case.postprocessor_failure_message,
            'expectations':  json.loads(render_to_json(case.specific_case.expectations)),
            'log': log_text,
        })


@bp.route('/case/delete', methods=['POST'])
@login_required
def delete_case():
    exist, form_case_id = get_form_from_request(request, 'case_id')
    if not exist: return form_case_id
    try:
        if Case.exist_and_status_not_delete(id=form_case_id):
            Case.delete(id=form_case_id)
        else:
            return jsonify({
                'error_no': -1,
                'error_msg': '错误信息 [' + '%s编号案例不存在或已删除' % form_case_id + ']',
            })
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/case/add', methods=['POST'])
def add_case():
    """添加新的测试案例组件"""
    exist, form_scene_id = get_form_from_request(request, 'scene_id')
    if not exist: return form_scene_id
    exist, form_case_type = get_form_from_request(request, 'case_type')
    if not exist: return form_case_type
    try:
        case = Scene.add_new_empty_case(scene_id=form_scene_id, case_type=form_case_type)
        if case is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '指定的测试场景编号%s不存在' % form_scene_id,
            })
        case_html = render_template('scene/_single_case.html', case=case)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'case_id': case.id,
            'case_html': case_html,
        })


@bp.route('/case/copy', methods=['POST'])
def copy_case():
    """在测试场景内复制案例组件"""
    exist, form_case_id = get_form_from_request(request, 'case_id')
    if not exist: return form_case_id
    exist, form_scene_id = get_form_from_request(request, 'scene_id')
    if not exist: return form_scene_id
    exist, form_logic_controller_id = get_form_from_request(request, 'logic_controller_id')
    if not exist: return form_logic_controller_id
    try:
        case = Case.copy_from_id(case_id=form_case_id,
                                 scene_id=form_scene_id,
                                 logic_controller_id=form_logic_controller_id)
        if case is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '复制测试案例组件失败',
            })
        case_html = render_template('scene/_single_case.html', case=case)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'case_html': case_html,
        })


@bp.route('/case/forbidden', methods=['POST'])
def forbidden_case():
    """禁用测试组件"""
    exist, form_id = get_form_from_request(request, 'id')
    if not exist: return form_id
    exist, form_type = get_form_from_request(request, 'type')
    if not exist: return form_type
    try:
        if form_type == 'forbidden':
            Case.forbidden(id=form_id)
        elif form_type == 'enable':
            Case.enable(id=form_id)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/tool/timer/save', methods=['POST'])
@login_required
def save_timer_tool():
    # 参数校验
    exist, form_tool_id = get_form_from_request(request, 'tool_id')
    if not exist: return form_tool_id
    exist, form_tool_name = get_form_from_request(request, 'tool_name')
    if not exist: return form_tool_name
    exist, form_tool_description = get_form_from_request(request, 'tool_description')
    if not exist: return form_tool_description
    exist, form_delay = get_form_from_request(request, 'delay')
    if not exist: return form_delay
    try:
        TimerTool.update(
            tool_id=form_tool_id,
            name=form_tool_name,
            description=form_tool_description,
            delay=form_delay,
        )
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/tool/script/save', methods=['POST'])
@login_required
def save_script_tool():
    # 参数校验
    exist, form_tool_id = get_form_from_request(request, 'tool_id')
    if not exist: return form_tool_id
    exist, form_tool_name = get_form_from_request(request, 'tool_name')
    if not exist: return form_tool_name
    exist, form_tool_description = get_form_from_request(request, 'tool_description')
    if not exist: return form_tool_description
    exist, form_script = get_form_from_request(request, 'script')
    if not exist: return form_script
    try:
        ScriptTool.update(
            tool_id=form_tool_id,
            name=form_tool_name,
            description=form_tool_description,
            script=form_script,
        )
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/tool/variable_definition/save', methods=['POST'])
@login_required
def save_variable_definition_tool():
    # 参数校验
    exist, form_tool_id = get_form_from_request(request, 'tool_id')
    if not exist: return form_tool_id
    exist, form_tool_name = get_form_from_request(request, 'tool_name')
    if not exist: return form_tool_name
    exist, form_tool_description = get_form_from_request(request, 'tool_description')
    if not exist: return form_tool_description
    exist, form_vars = get_form_from_request(request, 'vars')
    if not exist: return form_vars
    try:
        variables = json.loads(form_vars)
        VariableDefinitionTool.update(
            tool_id=form_tool_id,
            name=form_tool_name,
            description=form_tool_description,
            variables=variables,
        )
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/tool/http_header_manager/save', methods=['POST'])
@login_required
def save_http_header_manager_tool():
    # 参数校验
    exist, form_tool_id = get_form_from_request(request, 'tool_id')
    if not exist: return form_tool_id
    exist, form_tool_name = get_form_from_request(request, 'tool_name')
    if not exist: return form_tool_name
    exist, form_tool_description = get_form_from_request(request, 'tool_description')
    if not exist: return form_tool_description
    exist, form_vars = get_form_from_request(request, 'vars')
    if not exist: return form_vars
    try:
        variables = json.loads(form_vars)
        HTTPHeaderManagerTool.update(
            tool_id=form_tool_id,
            name=form_tool_name,
            description=form_tool_description,
            variables=variables,
        )
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/tool/http_cookie_manager/save', methods=['POST'])
@login_required
def save_http_cookie_manager_tool():
    # 参数校验
    exist, form_tool_id = get_form_from_request(request, 'tool_id')
    if not exist: return form_tool_id
    exist, form_tool_name = get_form_from_request(request, 'tool_name')
    if not exist: return form_tool_name
    exist, form_tool_description = get_form_from_request(request, 'tool_description')
    if not exist: return form_tool_description
    exist, form_attrs = get_form_from_request(request, 'attrs')
    if not exist: return form_attrs
    try:
        attributes = json.loads(form_attrs)
        HTTPCookieManagerTool.update(
            tool_id=form_tool_id,
            name=form_tool_name,
            description=form_tool_description,
            attributes=attributes,
        )
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/tool/script/run', methods=['POST'])
@login_required
def run_script_tool():
    # 参数校验
    exist, form_tool_id = get_form_from_request(request, 'tool_id')
    if not exist: return form_tool_id
    try:
        tool = Tool.query.filter_by(id=form_tool_id).first()
        if tool:
            if tool.status != STATUS.NORMAL:
                return jsonify({
                    'error_no': -1,
                    'error_msg': '错误信息 [脚本工具状态不是正常状态 tool_id=%s, status=%s]' % (form_tool_id, tool.status),
                })
            project_id = get_project_id_from_current_element(element=tool)
            result = ScriptToolDispatcher(tool=tool, dispatcher_type=DISPATCHER_TYPE.DEBUG, project_id=project_id).run()
            log_text = result.get('log_text', '')
        else:
            return jsonify({
                'error_no': -1,
                'error_msg': '错误信息 [未找到该脚本工具数据 tool_id=%s]' % form_tool_id,
            })
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'log_text': log_text,
            'error_msg': '',
        })


@bp.route('/tool/delete', methods=['POST'])
@login_required
def delete_tool():
    exist, form_tool_id = get_form_from_request(request, 'tool_id')
    if not exist: return form_tool_id
    try:
        if Tool.exist_and_status_not_delete(tool_id=form_tool_id):
            Tool.delete(tool_id=form_tool_id)
        else:
            return jsonify({
                'error_no': -1,
                'error_msg': '错误信息 [' + '%s编号工具不存在或已删除' % form_tool_id + ']',
            })
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/tool/copy', methods=['POST'])
def copy_tool():
    """复制工具组件"""
    exist, form_tool_id = get_form_from_request(request, 'tool_id')
    if not exist: return form_tool_id
    exist, form_logic_controller_id = get_form_from_request(request, 'logic_controller_id')
    if not exist: return form_logic_controller_id
    try:
        tool = Tool.copy_from_id(tool_id=form_tool_id,
                                 logic_controller_id=form_logic_controller_id)
        if tool is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '复制测试工具组件失败',
            })
        tool_html = render_template('scene/_single_tool.html', tool=tool)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'tool_html': tool_html,
        })


@bp.route('/tool/forbidden', methods=['POST'])
def forbidden_tool():
    """禁用测试组件"""
    exist, form_tool_id = get_form_from_request(request, 'tool_id')
    if not exist: return form_tool_id
    exist, form_type = get_form_from_request(request, 'type')
    if not exist: return form_type
    try:
        if form_type == 'forbidden':
            Tool.forbidden(tool_id=form_tool_id)
        elif form_type == 'enable':
            Tool.enable(tool_id=form_tool_id)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/logic_controller/add', methods=['POST'])
def add_logic_controller():
    """添加新的逻辑控制组件"""
    exist, form_parent_logic_controller_id = get_form_from_request(request, 'parent_logic_controller_id')
    if not exist: return form_parent_logic_controller_id
    exist, form_logic_controller_type = get_form_from_request(request, 'logic_controller_type')
    if not exist: return form_logic_controller_type
    try:
        if form_logic_controller_type == LOGIC_CONTROLLER_TYPE.IF_CONTROLLER:
            specific_controller = IfController.add(expression='',
                                                   parent_logic_controller_id=form_parent_logic_controller_id)
        elif form_logic_controller_type == LOGIC_CONTROLLER_TYPE.WHILE_CONTROLLER:
            specific_controller = WhileController.add(expression='',
                                                      parent_logic_controller_id=form_parent_logic_controller_id)
        elif form_logic_controller_type == LOGIC_CONTROLLER_TYPE.LOOP_CONTROLLER:
            specific_controller = LoopController.add(expression='',
                                                     parent_logic_controller_id=form_parent_logic_controller_id)
        elif form_logic_controller_type == LOGIC_CONTROLLER_TYPE.SIMPLE_CONTROLLER:
            specific_controller = SimpleController.add(parent_logic_controller_id=form_parent_logic_controller_id)
        else:
            raise ValueError("不支持添加此类逻辑控制器，logic_controller_type='%s'" % form_logic_controller_type)
        logic_controller_html = render_template('scene/_single_logic_controller.html',
                                                # specific_controller=specific_controller,
                                                logic_controller=specific_controller.logic_controller,
                                                LOGIC_CONTROLLER_TYPE=LOGIC_CONTROLLER_TYPE,
                                                include_single_logic_controller_type='add')
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'logic_controller_html': logic_controller_html,
            'logic_controller_id': specific_controller.logic_controller.id,
        })


@bp.route('/logic_controller/delete', methods=['POST'])
def delete_logic_controller():
    """删除逻辑控制器组件"""
    exist, form_logic_controller_id = get_form_from_request(request, 'logic_controller_id')
    if not exist: return form_logic_controller_id
    try:
        LogicController.delete(logic_controller_id=form_logic_controller_id)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/logic_controller/forbidden', methods=['POST'])
def forbidden_logic_controller():
    """禁用/启用逻辑控制器组件"""
    exist, form_logic_controller_id = get_form_from_request(request, 'logic_controller_id')
    if not exist: return form_logic_controller_id
    exist, form_type = get_form_from_request(request, 'type')
    if not exist: return form_type
    try:
        if form_type == 'forbidden':
            LogicController.forbidden(logic_controller_id=form_logic_controller_id)
        elif form_type == 'enable':
            LogicController.enable(logic_controller_id=form_logic_controller_id)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/logic_controller/copy', methods=['POST'])
def copy_logic_controller():
    """复制逻辑控制器组件"""
    exist, form_logic_controller_id = get_form_from_request(request, 'logic_controller_id')
    if not exist: return form_logic_controller_id
    exist, form_parent_logic_controller_id = get_form_from_request(request, 'parent_logic_controller_id')
    if not exist: return form_parent_logic_controller_id
    exist, form_scene_id = get_form_from_request(request, 'scene_id')
    if not exist: return form_scene_id
    try:
        logic_controller = LogicController.query.filter_by(id=form_logic_controller_id).first()
        if logic_controller is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '未在表 %s 找到当前逻辑控制器, logic_controller_id=%s' % ('logic_controller',
                                                                            form_logic_controller_id),
            })
        parent_logic_controller = LogicController.query.filter_by(id=form_parent_logic_controller_id).first()
        if parent_logic_controller is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '未在表 %s 找到当前逻辑控制器所属的父逻辑控制器, logic_controller_id=%s' % ('logic_controller',
                                                                                    form_parent_logic_controller_id),
            })
        # 先在父逻辑控制器新增一个逻辑控制器组件
        new_logic_controller = logic_controller.specific_controller.copy_from_self(
            parent_logic_controller_id=parent_logic_controller.id
        ).logic_controller
        # 将原逻辑控制器(logic_controller)内的组件复制到新的逻辑控制器中(new_logic_controller)
        LogicController.copy(original_logic_controller=logic_controller,
                             parent_logic_controller=new_logic_controller,
                             scene_id=form_scene_id)
        logic_controller_html = render_template('scene/_single_logic_controller.html',
                                                logic_controller=new_logic_controller,
                                                LOGIC_CONTROLLER_TYPE=LOGIC_CONTROLLER_TYPE,
                                                ELEMENT_TYPE=ELEMENT_TYPE,
                                                nested_logic_controller_id=new_logic_controller.id,
                                                include_single_logic_controller_type='copy')
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'logic_controller_html': logic_controller_html,
        })


@bp.route('/logic_controller/simple_controller/save', methods=['POST'])
def save_simple_controller():
    """保存Simple控制器数据"""
    exist, form_logic_controller_id = get_form_from_request(request, 'logic_controller_id')
    if not exist: return form_logic_controller_id
    exist, form_logic_controller_name = get_form_from_request(request, 'logic_controller_name')
    if not exist: return form_logic_controller_name
    exist, form_logic_controller_description = get_form_from_request(request, 'logic_controller_description')
    if not exist: return form_logic_controller_description
    try:
        SimpleController.update(logic_controller_id=form_logic_controller_id,
                                name=form_logic_controller_name,
                                description=form_logic_controller_description)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/logic_controller/if_controller/save', methods=['POST'])
def save_if_controller():
    """保存if控制器数据"""
    exist, form_logic_controller_id = get_form_from_request(request, 'logic_controller_id')
    if not exist: return form_logic_controller_id
    exist, form_logic_controller_name = get_form_from_request(request, 'logic_controller_name')
    if not exist: return form_logic_controller_name
    exist, form_logic_controller_expression = get_form_from_request(request, 'logic_controller_expression')
    if not exist: return form_logic_controller_expression
    exist, form_logic_controller_description = get_form_from_request(request, 'logic_controller_description')
    if not exist: return form_logic_controller_description
    try:
        IfController.update(logic_controller_id=form_logic_controller_id,
                            name=form_logic_controller_name,
                            description=form_logic_controller_description,
                            expression=form_logic_controller_expression)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/logic_controller/loop_controller/save', methods=['POST'])
def save_loop_controller():
    """保存loop控制器数据"""
    exist, form_logic_controller_id = get_form_from_request(request, 'logic_controller_id')
    if not exist: return form_logic_controller_id
    exist, form_logic_controller_name = get_form_from_request(request, 'logic_controller_name')
    if not exist: return form_logic_controller_name
    exist, form_logic_controller_description = get_form_from_request(request, 'logic_controller_description')
    if not exist: return form_logic_controller_description
    exist, form_logic_controller_expression = get_form_from_request(request, 'logic_controller_expression')
    if not exist: return form_logic_controller_expression
    try:
        LoopController.update(logic_controller_id=form_logic_controller_id,
                              name=form_logic_controller_name,
                              description=form_logic_controller_description,
                              expression=form_logic_controller_expression)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/logic_controller/while_controller/save', methods=['POST'])
def save_while_controller():
    """保存while控制器数据"""
    exist, form_logic_controller_id = get_form_from_request(request, 'logic_controller_id')
    if not exist: return form_logic_controller_id
    exist, form_logic_controller_name = get_form_from_request(request, 'logic_controller_name')
    if not exist: return form_logic_controller_name
    exist, form_logic_controller_description = get_form_from_request(request, 'logic_controller_description')
    if not exist: return form_logic_controller_description
    exist, form_logic_controller_expression = get_form_from_request(request, 'logic_controller_expression')
    if not exist: return form_logic_controller_expression
    try:
        WhileController.update(logic_controller_id=form_logic_controller_id,
                               name=form_logic_controller_name,
                               description=form_logic_controller_description,
                               expression=form_logic_controller_expression)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/tool/add', methods=['POST'])
def add_tool():
    """添加新的工具组件"""
    exist, form_parent_logic_controller_id = get_form_from_request(request, 'parent_logic_controller_id')
    if not exist: return form_parent_logic_controller_id
    exist, form_tool_type = get_form_from_request(request, 'tool_type')
    if not exist: return form_tool_type
    try:
        tool = Tool.add_new_empty_tool(parent_logic_controller_id=form_parent_logic_controller_id,
                                       tool_type=form_tool_type)
        tool_html = render_template('scene/_single_tool.html', tool=tool)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'tool_id': tool.id,
            'tool_html': tool_html,
        })


@bp.route('/project/get', methods=['POST'])
def get_project_list_data():
    projects = Project.query.all()
    ret = []
    for project in projects:
        if project.status == STATUS.DELETED:
            continue
        scheduler = Scheduler.get_scheduler(element_id=project.id, element_type=ELEMENT_TYPE.PROJECT)
        if scheduler is None:
            use_scheduler = False
            cron = ''
        else:
            use_scheduler = scheduler.enable
            cron = scheduler.cron
        ret.append({
            'id': project.id,
            'name': project.name,
            'description': project.description,
            'create_time': str(project.create_time),
            'last_updated_time': str(project.last_updated_time),
            'stop_on_error': project.project_advanced_configuration.stop_on_error,
            'clear_http_cookie': project.project_advanced_configuration.clear_http_cookie,
            'ding_talk_notify': project.project_advanced_configuration.ding_talk_notify,
            'email_notify': project.project_advanced_configuration.email_notify,
            'email_notify_when_default': project.project_advanced_configuration.email_notify_when_default,
            'email_notify_when_failure_or_error': project.project_advanced_configuration.email_notify_when_failure_or_error,
            'use_scheduler': use_scheduler,
            'cron': cron,
        })
    return jsonify(ret)


@bp.route('/project/add', methods=['POST'])
def add_project():
    # 参数校验
    exist, form_name = get_form_from_request(request, 'name')
    if not exist: return form_name
    exist, form_description = get_form_from_request(request, 'description')
    if not exist: return form_description
    try:
        Project.add(name=form_name, description=form_description)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/project/delete', methods=['POST'])
def delete_project():
    # 参数校验
    exist, form_id = get_form_from_request(request, 'id')
    if not exist: return form_id
    try:
        Project.delete(id=form_id)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/project/modify', methods=['POST'])
def modify_project():
    # 参数校验
    exist, form_id = get_form_from_request(request, 'id')
    if not exist: return form_id
    exist, form_name = get_form_from_request(request, 'name')
    if not exist: return form_name
    exist, form_description = get_form_from_request(request, 'description')
    if not exist: return form_description
    try:
        Project.update(id=form_id, name=form_name, description=form_description)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/project/config/modify', methods=['POST'])
def modify_project_config():
    # 参数校验
    exist, form_id = get_form_from_request(request, 'id')
    if not exist: return form_id
    exist, form_stop_on_error = get_form_from_request(request, 'stop_on_error')
    if not exist: return form_stop_on_error
    exist, form_ding_talk_notify = get_form_from_request(request, 'ding_talk_notify')
    if not exist: return form_ding_talk_notify
    exist, form_email_notify = get_form_from_request(request, 'email_notify')
    if not exist: return form_email_notify
    exist, form_email_notify_when_default = get_form_from_request(request, 'email_notify_when_default')
    if not exist: return form_email_notify_when_default
    exist, form_email_notify_when_failure_or_error = get_form_from_request(request, 'email_notify_when_failure_or_error')
    if not exist: return form_email_notify_when_failure_or_error
    exist, form_clear_http_cookie = get_form_from_request(request, 'clear_http_cookie')
    if not exist: return form_clear_http_cookie
    exist, form_use_scheduler = get_form_from_request(request, 'use_scheduler')
    if not exist: return form_use_scheduler
    exist, form_cron = get_form_from_request(request, 'cron')
    if not exist: return form_cron
    try:
        project = Project.query.filter_by(id=int(form_id)).first()
        if project is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '未找到该项目数据，项目id: %s' % form_id,
            })
        project.project_advanced_configuration.update(
            stop_on_error=form_stop_on_error.lower() == 'true',
            ding_talk_notify=form_ding_talk_notify.lower() == 'true',
            email_notify=form_email_notify.lower() == 'true',
            email_notify_when_default=form_email_notify_when_default.lower() == 'true',
            email_notify_when_failure_or_error=form_email_notify_when_failure_or_error.lower() == 'true',
            clear_http_cookie=form_clear_http_cookie.lower() == 'true',
        )
        # 设置定时任务
        job_id = 'project-' + form_id
        enable = form_use_scheduler.lower() == 'true'
        job = dispatcher_scheduler.get_job(job_id=job_id)
        if enable:  # 启用
            if job is None:
                dispatcher_scheduler.add_job(func=apscheduler_async_project_run,
                                             id=job_id,
                                             trigger=CronTrigger.from_crontab(form_cron),
                                             kwargs=dict(
                                                 project_id=form_id,
                                                 app=current_app._get_current_object(),
                                             ), )
            else:
                dispatcher_scheduler.modify_job(
                    job_id=job_id,
                    trigger=CronTrigger.from_crontab(form_cron),
                )
        else:  # 禁用
            if job is None:
                pass
            else:
                dispatcher_scheduler.remove_job(job_id=job_id)
        if Scheduler.get_scheduler(element_type=ELEMENT_TYPE.PROJECT, element_id=form_id) is None:
            Scheduler.add(element_type=ELEMENT_TYPE.PROJECT,
                          element_id=form_id,
                          enable=enable,
                          cron=form_cron)
        else:
            Scheduler.update(element_type=ELEMENT_TYPE.PROJECT,
                             element_id=form_id,
                             enable=enable,
                             cron=form_cron)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('project/config/crontab/test', methods=['POST'])
def test_crontab():
    # 参数校验
    exist, form_cron = get_form_from_request(request, 'cron')
    if not exist: return form_cron
    try:
        # CronTrigger.from_crontab(form_cron)  # 使用apscheduler.triggers.cron.CronTrigger对cron表达式合法性做判断
        # CronTrigger: https://apscheduler.readthedocs.io/en/stable/modules/triggers/cron.html#expression-types
        next_schedule_list = []
        date = datetime.now()
        for i in range(5):  # 最近5个计划时间
            cron_trigger = CronTrigger.from_crontab(form_cron)
            date = cron_trigger.get_next_fire_time(None, now=date + timedelta(microseconds=1))
            next_schedule_list.append(date.strftime('%Y-%m-%d %H:%M:%S'))
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'next_schedule_list': json.dumps(next_schedule_list, default=str),
        })


@bp.route('/project/test/start', methods=['POST'])
def start_project_test():
    """启动项目测试"""
    exist, form_project_id = get_form_from_request(request, 'project_id')
    if not exist: return form_project_id
    try:
        async_project_run(project_id=form_project_id)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/project/test/stop', methods=['POST'])
def stop_project_test():
    """终止项目测试"""
    exist, form_dispatcher_id = get_form_from_request(request, 'dispatcher_id')
    if not exist: return form_dispatcher_id
    try:
        dispatcher = Dispatcher.query.filter_by(id=form_dispatcher_id).first()
        if dispatcher is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '未找到项目调度数据',
            })
        if dispatcher.status in [DISPATCHER_STATUS.FINISHED, DISPATCHER_STATUS.STOPPED]:
            return jsonify({
                'error_no': -1,
                'error_msg': '当前测试状态为%s, 已经终止' % dispatcher.status,
            })
        else:
            dispatcher.update_status(status=DISPATCHER_STATUS.STOPPING)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/project/test/get', methods=['POST'])
def get_project_test():
    """查询项目测试当前状态和进度相关信息"""
    exist, form_project_id = get_form_from_request(request, 'project_id')
    if not exist: return form_project_id
    try:
        dispatcher = Dispatcher.query.filter_by(element_type=ELEMENT_TYPE.PROJECT, element_id=form_project_id)\
                                     .order_by(Dispatcher.id.desc())\
                                     .first()
        if dispatcher is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '未找到当前项目%s的调度信息' % form_project_id,
            })
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'dispatcher_id': dispatcher.id,
            'status': dispatcher.status,
            'progress': dispatcher.progress,
        })


@bp.route('/project/tree/get', methods=['POST'])
def get_project_tree():
    """查询项目案例树"""
    exist, form_project_id = get_form_from_request(request, 'project_id')
    if not exist: return form_project_id
    try:
        tree_data = []
        project = Project.query.filter_by(id=int(form_project_id)).first()
        if project is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '未查到项目数据, 项目id: %s' % form_project_id,
            })
        if project.status != STATUS.NORMAL:
            return jsonify({
                'error_no': -1,
                'error_msg': '项目状态不正常, 项目id: %s, 状态: %s' % (form_project_id, project.status),
            })
        project_children = []
        tree_data.append(dict(  # Fancytree NodeData属性
            title=str(project.id) + '_' + str(project.name),
            element_type=ELEMENT_TYPE.PROJECT,
            element_id=project.id,
            case_type=None,
            logic_controller_type=None,
            tool_type=None,
            children=project_children,
            checkbox=False,
        ))
        for module in sort_by_order_in_project(project.modules):
            if module.status in [STATUS.NORMAL, STATUS.FORBIDDEN]:
                module_children = []
                project_children.append(dict(
                    title=str(module.id) + '_' + str(module.name),
                    element_type=ELEMENT_TYPE.MODULE,
                    element_id=module.id,
                    case_type=None,
                    logic_controller_type=None,
                    tool_type=None,
                    children=module_children,
                    checkbox=False,
                ))
            else:
                continue
            for scene in sort_by_order_in_module(module.scenes):
                if scene.status in [STATUS.NORMAL, STATUS.FORBIDDEN]:
                    scene_children = []
                    module_children.append(dict(
                        title=str(scene.id) + '_' + str(scene.name),
                        element_type=ELEMENT_TYPE.SCENE,
                        element_id=scene.id,
                        case_type=None,
                        logic_controller_type=None,
                        tool_type=None,
                        children=scene_children,
                        checkbox=False,
                    ))
                else:
                    continue

                def _recursive_append_element_in_logic_controller(children, logic_controller_id):
                    for sub_element in sort_by_order_in_logic_controller(logic_controller_id=logic_controller_id):
                        if sub_element.element_type == ELEMENT_TYPE.CASE:
                            case = Case.query.filter_by(id=sub_element.element_id).first()
                            if case is not None and case.status in [STATUS.NORMAL, STATUS.FORBIDDEN]:
                                children.append(dict(
                                    title=str(case.id) + '_' + str(case.name),
                                    element_type=ELEMENT_TYPE.CASE,
                                    element_id=case.id,
                                    case_type=case.case_type,
                                    logic_controller_type=None,
                                    tool_type=None,
                                    children=[],
                                ))
                        elif sub_element.element_type == ELEMENT_TYPE.TOOL:
                            tool = Tool.query.filter_by(id=sub_element.element_id).first()
                            if tool is not None and tool.status in [STATUS.NORMAL, STATUS.FORBIDDEN]:
                                children.append(dict(
                                    title=str(tool.id) + '_' + str(tool.name),
                                    element_type=ELEMENT_TYPE.TOOL,
                                    element_id=tool.id,
                                    case_type=None,
                                    logic_controller_type=None,
                                    tool_type=tool.tool_type,
                                    children=[],
                                ))
                        elif sub_element.element_type == ELEMENT_TYPE.LOGIC_CONTROLLER:
                            logic_controller_children = []
                            logic_controller = LogicController.query.filter_by(id=sub_element.element_id).first()
                            if logic_controller is not None and logic_controller.status in [STATUS.NORMAL, STATUS.FORBIDDEN]:
                                children.append(dict(
                                    title=str(logic_controller.id) + '_' + str(logic_controller.name),
                                    element_type=ELEMENT_TYPE.LOGIC_CONTROLLER,
                                    element_id=logic_controller.id,
                                    case_type=None,
                                    logic_controller_type=logic_controller.logic_controller_type,
                                    tool_type=None,
                                    children=logic_controller_children,
                                ))
                            _recursive_append_element_in_logic_controller(children=logic_controller_children,
                                                                          logic_controller_id=sub_element.element_id)

                _recursive_append_element_in_logic_controller(
                    children=scene_children,
                    logic_controller_id=scene.scene_controller.logic_controller_id,
                )
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify(tree_data)


@bp.route('/module/get', methods=['POST'])
def get_module_list_data():
    # 参数校验
    exist, form_project_id = get_form_from_request(request, 'project_id')
    if not exist: return form_project_id

    project = Project.query.filter_by(id=form_project_id).first()
    if project is None:
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + '未找到该项目' + ']',
        })
    ret = []
    for module in sort_by_order_in_project(project.modules):
        if module.status == STATUS.DELETED:
            continue
        ret.append({
            'id': module.id,
            'name': module.name,
            'description': module.description,
            'creator': module.creator,
            'create_time': str(module.create_time),
            'last_updater': module.last_updater,
            'last_updated_time': str(module.last_updated_time)
        })
    return jsonify(ret)


@bp.route('/module/add', methods=['POST'])
def add_module():
    # 参数校验
    exist, form_project_id = get_form_from_request(request, 'project_id')
    if not exist: return form_project_id
    exist, form_name = get_form_from_request(request, 'name')
    if not exist: return form_name
    exist, form_description = get_form_from_request(request, 'description')
    if not exist: return form_description
    try:
        project = Project.query.filter_by(id=form_project_id).first()
        if project is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '错误信息 [' + '未找到该项目' + ']',
            })
        project.add_module(name=form_name, description=form_description)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/module/delete', methods=['POST'])
def delete_module():
    # 参数校验
    exist, form_project_id = get_form_from_request(request, 'project_id')
    if not exist: return form_project_id
    exist, form_id = get_form_from_request(request, 'id')
    if not exist: return form_id
    try:
        project = Project.query.filter_by(id=form_project_id).first()
        if project is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '错误信息 [' + '未找到该项目' + ']',
            })
        project.delete_module(id=form_id)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/module/modify', methods=['POST'])
def modify_module():
    # 参数校验
    exist, form_id = get_form_from_request(request, 'id')
    if not exist: return form_id
    exist, form_name = get_form_from_request(request, 'name')
    if not exist: return form_name
    exist, form_description = get_form_from_request(request, 'description')
    if not exist: return form_description
    try:
        Module.update(id=form_id, name=form_name, description=form_description)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/module/order', methods=['POST'])
def order_module():
    # 参数校验
    exist, form_data = get_form_from_request(request, 'data')
    if not exist: return form_data
    try:
        data = json.loads(form_data)
        Module.update_order_in_project(modules=data)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/module/test/start', methods=['POST'])
def start_module_test():
    """启动模块测试"""
    exist, form_module_id = get_form_from_request(request, 'module_id')
    if not exist: return form_module_id
    try:
        # module = Module.query.filter_by(id=form_module_id).first()
        # ModuleDispatcher(module=module).run()
        async_module_run(module_id=form_module_id)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/module/test/stop', methods=['POST'])
def stop_module_test():
    """终止模块测试"""
    exist, form_dispatcher_id = get_form_from_request(request, 'dispatcher_id')
    if not exist: return form_dispatcher_id
    try:
        dispatcher = Dispatcher.query.filter_by(id=form_dispatcher_id).first()
        if dispatcher is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '未找到模块调度数据',
            })
        if dispatcher.status in [DISPATCHER_STATUS.FINISHED, DISPATCHER_STATUS.STOPPED]:
            return jsonify({
                'error_no': -1,
                'error_msg': '当前测试状态为%s, 已终止' % dispatcher.status,
            })
        else:
            dispatcher.update_status(status=DISPATCHER_STATUS.STOPPING)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/module/test/get', methods=['POST'])
def get_module_test():
    """查询模块测试当前状态和进度相关信息"""
    exist, form_module_id = get_form_from_request(request, 'module_id')
    if not exist: return form_module_id
    try:
        dispatcher = Dispatcher.query.filter_by(element_type=ELEMENT_TYPE.MODULE, element_id=form_module_id)\
                                     .order_by(Dispatcher.id.desc())\
                                     .first()
        if dispatcher is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '未找到当前模块%s的调度信息' % form_module_id,
            })
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'dispatcher_id': dispatcher.id,
            'status': dispatcher.status,
            'progress': dispatcher.progress,
        })


@bp.route('/scene/add', methods=['POST'])
def add_scene():
    """添加新的测试场景"""
    exist, form_module_id = get_form_from_request(request, 'module_id')
    if not exist: return form_module_id
    try:
        scene = Module.add_new_empty_scene(module_id=form_module_id)
        if scene is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '指定的测试模块编号%s不存在' % form_module_id,
            })
        scene_html = render_template('scene/_single_scene.html', scene=scene)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'scene_html': scene_html,
            'scene_id': scene.id,
        })


@bp.route('/scene/copy', methods=['POST'])
def copy_scene():
    """复制一个新的测试场景"""
    exist, form_id = get_form_from_request(request, 'id')
    if not exist: return form_id
    try:
        scene = Scene.copy_from_id(id=form_id)
        if scene is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '复制测试场景失败',
            })
        scene_html = render_template('scene/_single_scene.html', scene=scene, ELEMENT_TYPE=ELEMENT_TYPE,
                                     LOGIC_CONTROLLER_TYPE=LOGIC_CONTROLLER_TYPE)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'scene_id': scene.id,
            'scene_html': scene_html,
        })


@bp.route('/scene/delete', methods=['POST'])
def delete_scene():
    """删除测试场景"""
    exist, form_id = get_form_from_request(request, 'id')
    if not exist: return form_id
    try:
        Scene.delete(id=form_id)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/scene/save', methods=['POST'])
def save_scene():
    """删除测试场景"""
    exist, form_id = get_form_from_request(request, 'id')
    if not exist: return form_id
    exist, form_name = get_form_from_request(request, 'name')
    if not exist: return form_name
    exist, form_description = get_form_from_request(request, 'description')
    if not exist: return form_description
    try:
        Scene.update(id=form_id, name=form_name, description=form_description)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/scene/forbidden', methods=['POST'])
def forbidden_scene():
    """禁用测试场景"""
    exist, form_id = get_form_from_request(request, 'id')
    if not exist: return form_id
    exist, form_type = get_form_from_request(request, 'type')
    if not exist: return form_type
    try:
        if form_type == 'forbidden':
            Scene.forbidden(id=form_id)
        elif form_type == 'enable':
            Scene.enable(id=form_id)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/scene/drag_scene', methods=['POST'])
def drag_scene():
    """拖拽测试场景后更新数据"""
    exist, form_module_id = get_form_from_request(request, 'module_id')
    if not exist: return form_module_id
    exist, form_scene_id_arr = get_form_from_request(request, 'scene_id_arr')
    if not exist: return form_scene_id_arr
    try:
        Module.reorder_scene(module_id=form_module_id, reorder_scene_id=json.loads(form_scene_id_arr))
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/scene/drag_element', methods=['POST'])
def drag_element():
    """在场景内拖拽组件更新数据"""
    exist, form_logic_controller_id = get_form_from_request(request, 'logic_controller_id')
    if not exist: return form_logic_controller_id
    exist, form_order_array = get_form_from_request(request, 'order_array')
    if not exist: return form_order_array
    try:
        elements_order_in_logic_controller = []
        order_array = json.loads(form_order_array)
        for index, order in enumerate(order_array):
            element_type, element_id = order.split('-')
            elements_order_in_logic_controller.append({'element_type': element_type,
                                                       'element_id': element_id,
                                                       'order_in_logic_controller': index,
                                                       'logic_controller_id': form_logic_controller_id})
        SubElementInLogicController.update_elements_order(
            elements_order_in_logic_controller=elements_order_in_logic_controller,
        )
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/report/get', methods=['POST'])
def get_report_list_data():
    reports = Report.query.filter(Report.status == STATUS.NORMAL).all()  # 只返回正常状态报告数据
    ret = []
    reports.sort(key=lambda report: report.id, reverse=True)   # 按照id从大到小排序
    for report in reports:
        ret.append(dict(
            id=report.id,
            name=report.name,
            result=report.result,
            start_time=str(report.dispatcher.start_time),
            elapsed_time=str(report.dispatcher.end_time - report.dispatcher.start_time),
            end_time=str(report.dispatcher.end_time) if report.result != REPORT_RESULT.RUNNING else '-',
        ))
    return jsonify(ret)


@bp.route('/report/latest/get', methods=['POST'])
def get_latest_reports():
    return jsonify({
            'error_no': 0,
            'error_msg': '',
            'html_text': render_template('_navbar_latest_reports.html'),
        })


@bp.route('/report/delete', methods=['POST'])
def delete_report():
    # 参数校验
    exist, form_id = get_form_from_request(request, 'id')
    if not exist: return form_id
    try:
        Report.update(id=form_id, status=STATUS.DELETED)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/report_detail/tree', methods=['POST'])
def get_report_detail_tree():
    exist, form_dispatcher_id = get_form_from_request(request, 'dispatcher_id')
    if not exist: return form_dispatcher_id
    try:
        tree_data = []
        dispatcher = Dispatcher.query.filter_by(id=int(form_dispatcher_id)).first()
        if dispatcher is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '未查到调度数据, 调度id: %s' % form_dispatcher_id,
            })
        status = dispatcher.status
        dispathcer_details = dispatcher.details
        if not dispathcer_details:
            return jsonify({
                'error_no': -1,
                'error_msg': '调度详情数据为空',
            })

        def _recurse_add_children_node(parent_dispatcher_detail_id, children):
            """递归添加调度元素及其子元素"""
            dispatcher_details = DispatcherDetail.query.filter_by(dispatcher_id=int(form_dispatcher_id),
                                                                  parent_dispatcher_detail_id=parent_dispatcher_detail_id).all()
            if not dispatcher_details:  # 如果没有找到子元素则直接返回
                return
            for dispatcher_detail in dispatcher_details:
                case_type = None
                case_result = None
                logic_controller_type = None
                tool_type = None
                if dispatcher_detail.element_type == ELEMENT_TYPE.CASE:
                    report_case_data = dispatcher_detail.specific_element_report_data
                    if report_case_data is not None:
                        case_type = report_case_data.case_type
                        case_result = report_case_data.result
                elif dispatcher_detail.element_type == ELEMENT_TYPE.LOGIC_CONTROLLER:
                    logic_controller = LogicController.query.filter_by(id=dispatcher_detail.element_id).first()
                    logic_controller_type = logic_controller.logic_controller_type if logic_controller else None
                elif dispatcher_detail.element_type == ELEMENT_TYPE.TOOL:
                    tool = Tool.query.filter_by(id=dispatcher_detail.element_id).first()
                    tool_type = tool.tool_type if tool else None
                new_children = []
                children.append(dict(
                    title=str(dispatcher_detail.element_name),
                    children=new_children,
                    element_id=dispatcher_detail.element_id,
                    element_type=dispatcher_detail.element_type,
                    dispatcher_detail_id=dispatcher_detail.id,
                    case_type=case_type,
                    case_result=case_result,
                    logic_controller_type=logic_controller_type,
                    tool_type=tool_type,
                ))
                _recurse_add_children_node(dispatcher_detail.id, new_children)
        root = dispathcer_details[0]
        root_children = []
        tree_data.append(dict(
            title=str(root.element_name),
            children=root_children,
            element_id=root.element_id,
            element_type=root.element_type,
            dispatcher_detail_id=root.id,
            case_type=None,
            case_result=None,
            logic_controller_type=None,
            tool_type=None,
        ))
        _recurse_add_children_node(root.id, root_children)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'tree_data': tree_data,
            'status': status,
        })


@bp.route('/report_detail/project', methods=['POST'])
def get_report_detail_project():
    exist, form_dispatcher_detail_id = get_form_from_request(request, 'dispatcher_detail_id')
    if not exist: return form_dispatcher_detail_id
    try:
        dispatcher_detail = DispatcherDetail.query.filter_by(id=int(form_dispatcher_detail_id),
                                                             element_type=ELEMENT_TYPE.PROJECT).first()
        if dispatcher_detail is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '未在调度详细表中找到项目数据',
            })
        html_text = render_template('report/_report_detail_project.html', dispatcher_detail=dispatcher_detail)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'html_text': html_text,
        })


@bp.route('/report_detail/module', methods=['POST'])
def get_report_detail_module():
    exist, form_dispatcher_detail_id = get_form_from_request(request, 'dispatcher_detail_id')
    if not exist: return form_dispatcher_detail_id
    try:
        dispatcher_detail = DispatcherDetail.query.filter_by(id=int(form_dispatcher_detail_id),
                                                             element_type=ELEMENT_TYPE.MODULE).first()
        if dispatcher_detail is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '未在调度详细表中找到模块数据',
            })
        html_text = render_template('report/_report_detail_module.html', dispatcher_detail=dispatcher_detail)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'html_text': html_text,
        })


@bp.route('/report_detail/scene', methods=['POST'])
def get_report_detail_scene():
    exist, form_dispatcher_detail_id = get_form_from_request(request, 'dispatcher_detail_id')
    if not exist: return form_dispatcher_detail_id
    try:
        dispatcher_detail = DispatcherDetail.query.filter_by(id=int(form_dispatcher_detail_id),
                                                             element_type=ELEMENT_TYPE.LOGIC_CONTROLLER).first()
        if dispatcher_detail is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '未在调度详细表中找到场景数据',
            })
        html_text = render_template('report/_report_detail_scene.html', dispatcher_detail=dispatcher_detail)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'html_text': html_text
        })


@bp.route('/report_detail/logic_controller', methods=['POST'])
def get_report_detail_logic_controller():
    exist, form_dispatcher_detail_id = get_form_from_request(request, 'dispatcher_detail_id')
    if not exist: return form_dispatcher_detail_id
    try:
        dispatcher_detail = DispatcherDetail.query.filter_by(id=int(form_dispatcher_detail_id),
                                                             element_type=ELEMENT_TYPE.LOGIC_CONTROLLER).first()
        if dispatcher_detail is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '未在调度详细表中找到逻辑控制器数据',
            })
        logic_controller = LogicController.query.filter_by(id=dispatcher_detail.element_id).first()
        if logic_controller.logic_controller_type == LOGIC_CONTROLLER_TYPE.SIMPLE_CONTROLLER:
            html_text = render_template('report/_report_detail_simple_controller.html',
                                        dispatcher_detail=dispatcher_detail,
                                        logic_controller=logic_controller)
        elif logic_controller.logic_controller_type == LOGIC_CONTROLLER_TYPE.IF_CONTROLLER:
            html_text = render_template('report/_report_detail_if_controller.html',
                                        dispatcher_detail=dispatcher_detail,
                                        logic_controller=logic_controller)
        elif logic_controller.logic_controller_type == LOGIC_CONTROLLER_TYPE.LOOP_CONTROLLER:
            html_text = render_template('report/_report_detail_loop_controller.html',
                                        dispatcher_detail=dispatcher_detail,
                                        logic_controller=logic_controller)
        elif logic_controller.logic_controller_type == LOGIC_CONTROLLER_TYPE.WHILE_CONTROLLER:
            html_text = render_template('report/_report_detail_while_controller.html',
                                        dispatcher_detail=dispatcher_detail,
                                        logic_controller=logic_controller)
        else:
            html_text = ''
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'html_text': html_text,
        })


@bp.route('/report_detail/case', methods=['POST'])
def get_report_detail_case():
    exist, form_dispatcher_detail_id = get_form_from_request(request, 'dispatcher_detail_id')
    if not exist: return form_dispatcher_detail_id
    try:
        dispatcher_detail = DispatcherDetail.query.filter_by(id=int(form_dispatcher_detail_id),
                                                             element_type=ELEMENT_TYPE.CASE).first()
        if dispatcher_detail is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '未在调度详细表中找到案例数据',
            })
        report_case_data = dispatcher_detail.specific_element_report_data
        if report_case_data is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '报告数据表中无该案例数据',
            })
        case_expectation_result = dict(total_count=0, success_count=0, failure_count=0, result=False)
        case_expectation_result['total_count'] = len(report_case_data.report_case_expectation_data)
        case_expectation_result['success_count'] = len(list(filter(lambda row: row.result,
                                                              report_case_data.report_case_expectation_data)))
        case_expectation_result['failure_count'] = len(list(filter(lambda row: not row.result,
                                                              report_case_data.report_case_expectation_data)))
        if report_case_data.expectation_logic == EXPECTATION_LOGIC.AND:
            case_expectation_result['result'] = case_expectation_result['total_count'] == case_expectation_result['success_count']
        else:
            case_expectation_result['result'] = case_expectation_result['success_count'] > 0
        method = 'unknown'
        if report_case_data.case_type in [CASE_TYPE.SSH, CASE_TYPE.HTTP]:
            method = Case.query.filter_by(id=report_case_data.case_id).first().specific_case.method
        elif report_case_data.case_type in [CASE_TYPE.SQL]:
            method = Case.query.filter_by(id=report_case_data.case_id).first().specific_case.db_type
        elif report_case_data.case_type in [CASE_TYPE.DEBUG]:
            method = 'Debug'
        html_text = render_template('report/_report_detail_case.html', dispatcher_detail=dispatcher_detail,
                                    report_case_data=report_case_data, case_expectation_result=case_expectation_result,
                                    post_processor_result=report_case_data.postprocessor_result, method=method,
                                    REPORT_RESULT=REPORT_RESULT)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'expectations': json.loads(render_to_json(report_case_data.report_case_expectation_data)),
            'html_text': html_text,
        })


@bp.route('/report_detail/tool', methods=['POST'])
def get_report_detail_tool():
    exist, form_dispatcher_detail_id = get_form_from_request(request, 'dispatcher_detail_id')
    if not exist: return form_dispatcher_detail_id
    try:
        dispatcher_detail = DispatcherDetail.query.filter_by(id=int(form_dispatcher_detail_id),
                                                             element_type=ELEMENT_TYPE.TOOL).first()
        if dispatcher_detail is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '未在调度详细表中找到工具数据',
            })
        report_tool_data = dispatcher_detail.specific_element_report_data
        variables = []
        attributes = []
        if report_tool_data.tool_type == TOOL_TYPE.TIMER:
            html_text = render_template('report/_report_detail_timer_tool.html',
                                        dispatcher_detail=dispatcher_detail,
                                        report_tool_data=report_tool_data)
        elif report_tool_data.tool_type == TOOL_TYPE.SCRIPT:
            html_text = render_template('report/_report_detail_script_tool.html',
                                        dispatcher_detail=dispatcher_detail,
                                        report_tool_data=report_tool_data)
        elif report_tool_data.tool_type == TOOL_TYPE.VARIABLE_DEFINITION:
            html_text = render_template('report/_report_detail_variable_definition_tool.html',
                                        dispatcher_detail=dispatcher_detail,
                                        report_tool_data=report_tool_data)
            variables = report_tool_data.report_variable_definition_tool_data.variables
        elif report_tool_data.tool_type == TOOL_TYPE.HTTP_HEADER_MANAGER:
            html_text = render_template('report/_report_detail_http_header_manager_tool.html',
                                        dispatcher_detail=dispatcher_detail,
                                        report_tool_data=report_tool_data)
            variables = report_tool_data.report_http_header_manager_tool_data.variables
        elif report_tool_data.tool_type == TOOL_TYPE.HTTP_COOKIE_MANAGER:
            html_text = render_template('report/_report_detail_http_cookie_manager_tool.html',
                                        dispatcher_detail=dispatcher_detail,
                                        report_tool_data=report_tool_data)
            attributes = report_tool_data.report_http_cookie_manager_tool_data.attributes
        else:
            html_text = ''
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'html_text': html_text,
            'tool_type': report_tool_data.tool_type,
            'variables': variables,
            'attributes': attributes,
        })


@bp.route('/report_detail/log/get', methods=['POST'])
def get_report_detail_log():
    exist, form_dispatcher_id = get_form_from_request(request, 'dispatcher_id')
    if not exist: return form_dispatcher_id
    exist, form_row_num = get_form_from_request(request, 'row_num')
    if not exist: return form_row_num
    try:
        dispatcher = Dispatcher.query.filter_by(id=form_dispatcher_id).first()
        if dispatcher is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '未在调度表中找到当前调度数据, dispatcher_id=%s' % form_dispatcher_id,
            })
        status = dispatcher.status
        content = dispatcher.log.split('\n')[int(form_row_num)-1::]
        content = '\n'.join(content)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'status': status,
            'content': content,
        })


@bp.route('/report_detail/case_result_count/get', methods=['POST'])
def get_case_result_count():
    exist, form_dispatcher_id = get_form_from_request(request, 'dispatcher_id')
    if not exist: return form_dispatcher_id
    try:
        dispatcher = Dispatcher.query.filter_by(id=form_dispatcher_id).first()
        if dispatcher is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '未在调度表中找到当前调度数据, dispatcher_id=%s' % form_dispatcher_id,
            })
        status = dispatcher.status
        report = dispatcher.report
        if report is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '未找到调度相关报告数据, dispatcher_id=%s' % form_dispatcher_id,
            })
        report_case_data = report.report_case_data
        success_count = 0
        failure_count = 0
        error_count = 0
        skip_count = 0
        case_count = 0
        for data in report_case_data:
            case_count += 1
            if data.result == REPORT_RESULT.SUCCESS:
                success_count += 1
            elif data.result == REPORT_RESULT.FAILURE:
                failure_count += 1
            elif data.result == REPORT_RESULT.ERROR:
                error_count += 1
            elif data.result == REPORT_RESULT.SKIP:
                skip_count += 1
        case_result_count_data = [
            {'name': REPORT_RESULT.SUCCESS, 'value': success_count},
            {'name': REPORT_RESULT.FAILURE, 'value': failure_count},
            {'name': REPORT_RESULT.ERROR, 'value': error_count},
            {'name': REPORT_RESULT.SKIP, 'value': skip_count},
        ]
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'status': status,
            'case_result_count_data': case_result_count_data,
        })


@bp.route('/scene', methods=['POST'])
def scene():
    exist, form_scene_id = get_form_from_request(request, 'scene_id')
    if not exist: return form_scene_id
    try:
        scene = Scene.query.filter_by(id=form_scene_id).first()
        if scene is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '错误信息 [未找到场景数据scene_id=%s]' % form_scene_id,
            })
        html_text = render_template('scene/scene_setting.html', scene=scene)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'html_text': html_text,
            'scene_id': scene.id,
        })


@bp.route('/logic_controller', methods=['POST'])
def logic_controller():
    exist, form_logic_controller_id = get_form_from_request(request, 'logic_controller_id')
    if not exist: return form_logic_controller_id
    try:
        logic_controller = LogicController.query.filter_by(id=form_logic_controller_id).first()
        if logic_controller is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '错误信息 [未找到逻辑控制器数据logic_controller_id=%s]' % form_logic_controller_id,
            })
        if logic_controller.logic_controller_type == LOGIC_CONTROLLER_TYPE.IF_CONTROLLER:
            html_text = render_template('logic_controller/if.html', logic_controller=logic_controller)
        elif logic_controller.logic_controller_type == LOGIC_CONTROLLER_TYPE.LOOP_CONTROLLER:
            html_text = render_template('logic_controller/loop.html', logic_controller=logic_controller)
        elif logic_controller.logic_controller_type == LOGIC_CONTROLLER_TYPE.WHILE_CONTROLLER:
            html_text = render_template('logic_controller/while.html', logic_controller=logic_controller)
        elif logic_controller.logic_controller_type == LOGIC_CONTROLLER_TYPE.SIMPLE_CONTROLLER:
            html_text = render_template('logic_controller/simple.html', logic_controller=logic_controller)
        else:
            return jsonify({
                'error_no': -1,
                'error_msg': '错误信息 [未知的逻辑控制类型logic_controller_type=%s]' % logic_controller.logic_controller_type,
            })
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'html_text': html_text,
            'logic_controller_id': logic_controller.id,
            'logic_controller_type': logic_controller.logic_controller_type,
            'specific_controller_id': logic_controller.specific_controller.id,
        })


@bp.route('/case', methods=['POST'])
def case():
    exist, form_case_id = get_form_from_request(request, 'case_id')
    if not exist: return form_case_id
    try:
        case = Case.query.filter_by(id=form_case_id).first()
        if case is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '错误信息 [未找案例数据case_id=%s]' % form_case_id,
            })
        if case.case_type == CASE_TYPE.SSH:
            html_text = render_template('case/ssh.html', case=case)
        elif case.case_type == CASE_TYPE.SQL:
            html_text = render_template('case/sql.html', case=case)
        elif case.case_type == CASE_TYPE.HTTP:
            html_text = render_template('case/http/http.html', case=case, CONTENT_TYPE=CONTENT_TYPE)
        elif case.case_type == CASE_TYPE.DEBUG:
            html_text = render_template('case/debug.html', case=case)
        else:
            return jsonify({
                'error_no': -1,
                'error_msg': '错误信息 [未知的案例类型case_type=%s]' % case.case_type,
            })
        data_expectations = json.loads(render_to_json(case.specific_case.expectations))
        if case.case_type == CASE_TYPE.HTTP:
            data_parameters = json.loads(render_to_json(case.specific_case.parameters))
            data_headers = json.loads(render_to_json(case.specific_case.headers))
            data_file_upload = json.loads(render_to_json(case.specific_case.file_upload))
            table_cookie_manager_data = json.loads(render_to_json(case.specific_case.file_upload))
        else:
            data_parameters = []
            data_headers = []
            data_file_upload = []
            table_cookie_manager_data = []
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'case_id': case.id,
            'case_type': case.case_type,
            'html_text': html_text,
            'data_expectations': data_expectations,
            'data_parameters': data_parameters,
            'data_headers': data_headers,
            'data_file_upload': data_file_upload,
            'table_cookie_manager_data': table_cookie_manager_data,
        })


@bp.route('/tool', methods=['POST'])
def tool():
    exist, form_tool_id = get_form_from_request(request, 'tool_id')
    if not exist: return form_tool_id
    try:
        tool = Tool.query.filter_by(id=form_tool_id).first()
        if tool is None:
            return jsonify({
                'error_no': -1,
                'error_msg': '错误信息 [未找工具数据tool_id=%s]' % form_tool_id,
            })
        data_variables = []
        data_attributes = []
        if tool.tool_type == TOOL_TYPE.TIMER:
            html_text = render_template('tool/timer.html', tool=tool)
        elif tool.tool_type == TOOL_TYPE.SCRIPT:
            html_text = render_template('tool/script.html', tool=tool)
        elif tool.tool_type == TOOL_TYPE.VARIABLE_DEFINITION:
            html_text = render_template('tool/variable_definition.html', tool=tool)
            data_variables = json.loads(render_to_json(tool.specific_tool.variable_definition_list))
        elif tool.tool_type == TOOL_TYPE.HTTP_HEADER_MANAGER:
            html_text = render_template('tool/http_header_manager.html', tool=tool)
            data_variables = json.loads(render_to_json(tool.specific_tool.http_header_manager_list))
        elif tool.tool_type == TOOL_TYPE.HTTP_COOKIE_MANAGER:
            html_text = render_template('tool/http_cookie_manager.html', tool=tool)
            data_attributes = json.loads(render_to_json(tool.specific_tool.http_cookie_manager_list))
        else:
            return jsonify({
                'error_no': -1,
                'error_msg': '错误信息 [未知的工具类型tool_type=%s]' % tool.tool_type,
            })
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
            'tool_id': tool.id,
            'tool_type': tool.tool_type,
            'html_text': html_text,
            'data_variables': data_variables,
            'data_attributes': data_attributes,
        })


@bp.route('/setting/ding_talk_robot_setting/get', methods=['POST'])
def get_ding_talk_robot_setting():
    try:
        ding_talk_robot_settings = DingTalkRobotSetting.query.all()
        ret = []
        for ding_talk_robot_setting in ding_talk_robot_settings:
            association_projects = DingTalkRobotSettingAssociationProject.query.filter_by(setting_id=ding_talk_robot_setting.id).all()
            projects = [row.project_id for row in association_projects]
            ret.append({
                'id': ding_talk_robot_setting.id,
                'enable': ding_talk_robot_setting.enable,
                'access_token': ding_talk_robot_setting.access_token,
                'secret': ding_talk_robot_setting.secret,
                'at_all': ding_talk_robot_setting.at_all,
                'at_mobiles': ding_talk_robot_setting.at_mobiles,
                'projects': projects,
            })
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify(ret)


@bp.route('/setting/ding_talk_robot_setting/add', methods=['POST'])
def add_ding_talk_robot_setting():
    exist, form_enable = get_form_from_request(request, 'enable')
    if not exist: return form_enable
    exist, form_at_all = get_form_from_request(request, 'at_all')
    if not exist: return form_at_all
    exist, form_projects = get_form_from_request(request, 'projects')
    if not exist: return form_projects
    exist, form_access_token = get_form_from_request(request, 'access_token')
    if not exist: return form_access_token
    exist, form_secret = get_form_from_request(request, 'secret')
    if not exist: return form_secret
    exist, form_at_mobiles = get_form_from_request(request, 'at_mobiles')
    if not exist: return form_at_mobiles
    try:
        projects = json.loads(form_projects)
        DingTalkRobotSetting.add(
            enable=form_enable.lower() == 'true',
            at_all=form_at_all.lower() == 'true',
            access_token=form_access_token,
            secret=form_secret,
            at_mobiles=form_at_mobiles,
            projects=projects,
        )
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/setting/ding_talk_robot_setting/modify', methods=['POST'])
def modify_ding_talk_robot_setting():
    exist, form_id = get_form_from_request(request, 'id')
    if not exist: return form_id
    exist, form_enable = get_form_from_request(request, 'enable')
    if not exist: return form_enable
    exist, form_at_all = get_form_from_request(request, 'at_all')
    if not exist: return form_at_all
    exist, form_projects = get_form_from_request(request, 'projects')
    if not exist: return form_projects
    exist, form_access_token = get_form_from_request(request, 'access_token')
    if not exist: return form_access_token
    exist, form_secret = get_form_from_request(request, 'secret')
    if not exist: return form_secret
    exist, form_at_mobiles = get_form_from_request(request, 'at_mobiles')
    if not exist: return form_at_mobiles
    try:
        projects = json.loads(form_projects)
        DingTalkRobotSetting.modify(
            id=form_id,
            enable=form_enable.lower() == 'true',
            at_all=form_at_all.lower() == 'true',
            access_token=form_access_token,
            secret=form_secret,
            at_mobiles=form_at_mobiles,
            projects=projects,
        )
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/setting/ding_talk_robot_setting/delete', methods=['POST'])
def delete_ding_talk_robot_setting():
    exist, form_id = get_form_from_request(request, 'id')
    if not exist: return form_id
    try:
        DingTalkRobotSetting.delete(id=form_id)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/setting/email_receiver_setting/get', methods=['POST'])
def get_email_receiver_setting():
    try:
        email_receiver_settings = EmailReceiverSetting.query.all()
        ret = []
        for email_receiver_setting in email_receiver_settings:
            association_projects = EmailReceiverSettingAssociationProject.query.filter_by(setting_id=email_receiver_setting.id).all()
            projects = [row.project_id for row in association_projects]
            ret.append({
                'id': email_receiver_setting.id,
                'enable': email_receiver_setting.enable,
                'name': email_receiver_setting.name,
                'address': email_receiver_setting.address,
                'projects': projects,
            })
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify(ret)


@bp.route('/setting/email_receiver_setting/add', methods=['POST'])
def add_email_receiver_setting():
    exist, form_enable = get_form_from_request(request, 'enable')
    if not exist: return form_enable
    exist, form_projects = get_form_from_request(request, 'projects')
    if not exist: return form_projects
    exist, form_name = get_form_from_request(request, 'name')
    if not exist: return form_name
    exist, form_address = get_form_from_request(request, 'address')
    if not exist: return form_address
    try:
        projects = json.loads(form_projects)
        EmailReceiverSetting.add(
            enable=form_enable.lower() == 'true',
            name=form_name,
            address=form_address,
            projects=projects,
        )
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/setting/email_receiver_setting/modify', methods=['POST'])
def modify_email_receiver_setting():
    exist, form_id = get_form_from_request(request, 'id')
    if not exist: return form_id
    exist, form_enable = get_form_from_request(request, 'enable')
    if not exist: return form_enable
    exist, form_projects = get_form_from_request(request, 'projects')
    if not exist: return form_projects
    exist, form_name = get_form_from_request(request, 'name')
    if not exist: return form_name
    exist, form_address = get_form_from_request(request, 'address')
    if not exist: return form_address
    try:
        projects = json.loads(form_projects)
        EmailReceiverSetting.modify(
            id=form_id,
            enable=form_enable.lower() == 'true',
            name=form_name,
            address=form_address,
            projects=projects,
        )
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })


@bp.route('/setting/email_receiver_setting/delete', methods=['POST'])
def delete_email_receiver_setting():
    exist, form_id = get_form_from_request(request, 'id')
    if not exist: return form_id
    try:
        EmailReceiverSetting.delete(id=form_id)
    except Exception as e:
        exception_handle(current_app, e)
        return jsonify({
            'error_no': -1,
            'error_msg': '错误信息 [' + repr(e) + ']',
        })
    else:
        return jsonify({
            'error_no': 0,
            'error_msg': '',
        })
